#pragma once

#include <sstream>
#include <string>
#include <memory>
#include <vector>
#include "common/utils.h"

/**
  * to implement a state machine by state-pattern.
  */

namespace lexer {

class FiniteStateMachine;

/**
  * Abstract State
  */
class AtomicState 
{
public:
    virtual void Execute(FiniteStateMachine *) = 0;

public:
    static std::shared_ptr<AtomicState> STATE_0;
    static std::shared_ptr<AtomicState> STATE_1;
    static std::shared_ptr<AtomicState> STATE_2;
    static std::shared_ptr<AtomicState> STATE_3;
    static std::shared_ptr<AtomicState> STATE_4;
    static std::shared_ptr<AtomicState> STATE_5;
    static std::shared_ptr<AtomicState> STATE_6;
    static std::shared_ptr<AtomicState> STATE_7;
    static std::shared_ptr<AtomicState> STATE_8;
};


/** 
  * Finite State Machine Context Class 
  */
class FiniteStateMachine 
{
public:
    void Start();
    void RunState(std::shared_ptr<AtomicState> state);
    const std::vector<common::WordToken>& GetTokens();
    const std::string& GetSrcCode();
    std::string GetContext();
    int GetContextIndex() const;
    void IncreaseContext();
    void AddToken(common::WordType tp);
    void SetSrcCode(const std::string &src);
    void SetContextIndex(int idx);

private:
    std::shared_ptr<AtomicState> mNextState = nullptr;
    bool                         mIsOperating = false;
    std::stringstream            mContext;
    std::string                  mSrcCode;
    int                          mContextIndex = 0;
    std::vector<common::WordToken> mTokens;
};


/** 
  * Concrete States 
  */
class AtomicState_0 : public AtomicState 
{
public:
    void Execute(FiniteStateMachine *) override;
};

class AtomicState_1 : public AtomicState 
{
public:
    void Execute(FiniteStateMachine *) override;
};

class AtomicState_2 : public AtomicState 
{
public:
    void Execute(FiniteStateMachine *) override;
};

class AtomicState_3 : public AtomicState 
{
public:
    void Execute(FiniteStateMachine *) override;
};

class AtomicState_4 : public AtomicState 
{
public:
    void Execute(FiniteStateMachine *) override;
};

class AtomicState_5 : public AtomicState 
{
public:
    void Execute(FiniteStateMachine *) override;
};

class AtomicState_6 : public AtomicState 
{
public:
    void Execute(FiniteStateMachine *) override;
};

class AtomicState_7 : public AtomicState 
{
public:
    void Execute(FiniteStateMachine *) override;
};

class AtomicState_8 : public AtomicState 
{
public:
    void Execute(FiniteStateMachine *) override;
};

} // namespace lexer